#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

#include <asm/io.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/types.h>

#define DRVNAME "key_deriver"
#define IRQ_NAME "key_irq"
#define DEV_NAME "key_device"
#define Key_Class_Name "ChenGreenKeyClass"
#define KEY_DEVICE_NAME "ChenGreenKeyDeviceName"

static int dev_gpio;
static int irq_number;

static dev_t dev_no; // dev_t 设备号 32 位，其中，高12位表示主设备号，低20位表示次设备号
static struct cdev chardev_str; // 字符设备结构体
static struct class* irq_class;
static struct device* irq_dev;
atomic_t key_status = ATOMIC_INIT(0);

static irqreturn_t key_irq_hander(int irq, void* dev_id)
{
	// printk_green("button on \n");
	/*按键状态加一*/
	atomic_inc(&key_status);
	return IRQ_HANDLED;
}

static int key_open(struct inode* inode, struct file* file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static int key_close(struct inode* inode, struct file* file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static ssize_t key_read(struct file* file, char __user* buff, size_t count, loff_t* offp)
{
	int ret;
	int key_countervc = 0;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	key_countervc = atomic_read(&key_status);
	ret = copy_to_user(buff, &key_countervc, sizeof(key_countervc));
	atomic_set(&key_status, 0);
	return 0;
}

static struct file_operations irq_fops = {
	.owner = THIS_MODULE,
	.open = key_open,
	.release = key_close,
	.read = key_read,
};

static int key_probe(struct platform_device* pdev)
{
	dev_gpio = of_get_named_gpio(pdev->dev.of_node, "gpios", 0);
	if (dev_gpio < 0) {
		printk("can not get dev_gpio! \n");
	}
	printk("dev_gpio =  %d \n", dev_gpio);

	// gpio_request(dev_gpio, "dev_gpio");
	// gpio_direction_output(dev_gpio,0);

	irq_number = gpio_to_irq(dev_gpio);
	printk("the number of irq =  %d \n", irq_number);
	request_irq(irq_number, key_irq_hander, IRQF_TRIGGER_FALLING, IRQ_NAME, irq_dev);

	alloc_chrdev_region(&dev_no, 0, 1, DEV_NAME);
	cdev_init(&chardev_str, &irq_fops);
	chardev_str.owner = THIS_MODULE;
	cdev_add(&chardev_str, dev_no, 1);

	irq_class = class_create(THIS_MODULE, Key_Class_Name);
	irq_dev = device_create(irq_class, NULL, dev_no, NULL, KEY_DEVICE_NAME);

	printk("key init finished! \n");
	return 0;
}

static int key_remove(struct platform_device* dev)
{
	free_irq(irq_number, irq_dev);
	gpio_free(dev_gpio);

	cdev_del(&chardev_str);
	unregister_chrdev_region(dev_no, 1);
	device_destroy(irq_class, dev_no);
	class_destroy(irq_class);
	printk("key_exit finished! \n");
	return 0;
}

static const struct of_device_id drv_compatible[] = {
	{
	    .compatible = "chengreen_keys",
	},
	{},
};

static struct platform_driver key_drv = {
	.probe = key_probe,
	.remove = key_remove,
	.driver = {
	    .name = DRVNAME,
	    .owner = THIS_MODULE,
	    .of_match_table = drv_compatible,
	},
};

static int __init key_irq_init(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	platform_driver_register(&key_drv); // 注册一个平台驱动
	return 0;
}

static void __exit key_irq_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	platform_driver_unregister(&key_drv);
}

module_init(key_irq_init);
module_exit(key_irq_exit);
MODULE_LICENSE("GPL");
